import java.util.PriorityQueue;

//要有一个类来描述要执行的任务
class TimerTask implements Comparable<TimerTask>{
    //要描述执行任务的内容
    private Runnable task;
    //执行任务的延时
    private long executeTime;
    public TimerTask(Runnable runnable, long delay){
        this.task = runnable;
        this.executeTime = System.currentTimeMillis() + delay;
    }

    public Runnable getTask() {
        return task;
    }

    public long getExecuteTime(){
        return executeTime;
    }

    //实现Comparable接口，重写compareTo方法，实例化时成为可比较的对象
    @Override
    public int compareTo(TimerTask o) {
        return (int) (this.executeTime - o.executeTime);
    }
}
//模拟实现定时器
public class MyTimer {
    //首先要有一个队列存放要执行的任务
    //定义优先级队列，要给定一个比较对象的方式，但是因为类的属性都是封装的，因此不能使用比较器，只能传可比较的对象
    private PriorityQueue<TimerTask> queue = new PriorityQueue<>();
    //考虑到线程安全问题，需要对修改队列的方法进行加锁，这里定义锁
    private Object locker = new Object();
    //定义增加任务的方法
    public void schedule(TimerTask task){
        synchronized (locker){
            queue.offer(task);
            //唤醒任务为空时的阻塞等待，唤醒没到任务执行时间的阻塞等待，刷新最新任务的时间
            locker.notify();
        }
    }
    //在创建定时器的时候，需要有一个线程持续扫描定时器的任务
    public MyTimer(){
        Thread t = new Thread(() -> {
            //需要持续扫描，这里使用while循环
            while(true){
                try{
                    synchronized (locker){
                        //检查任务队列是否有可执行的任务
                        //每次唤醒之后，还需要再次确认队列是否为空，因此这里使用while循环
                        while(queue.isEmpty()){
                            //任务队列为空，进行阻塞等待
                            locker.wait();
                        }
                        //队列不为空，要检查堆顶任务是否到任务执行时间
                        TimerTask task = queue.peek();
                        long timeNow = System.currentTimeMillis();
                        if(timeNow >= task.getExecuteTime()){
                            task.getTask().run();
                            //任务执行完成，要在任务队列中删除
                            queue.poll();
                        }else{
                            //如果没到任务执行时间，也不要忙等，让线程进行休眠状态
                            locker.wait(task.getExecuteTime() - timeNow);
                        }
                    }
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        },"taskScanner");
        t.start();
    }
}

class MyTimerTest{
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new TimerTask(new Runnable() {
            @Override
            public void run() {
                System.out.println("3000");
            }
        }, 3000));
        timer.schedule(new TimerTask(new Runnable() {
            @Override
            public void run() {
                System.out.println("2000");
            }
        }, 2000));
        timer.schedule(new TimerTask(new Runnable() {
            @Override
            public void run() {
                System.out.println(1000);
            }
        }, 1000));
        System.out.println("程序开始执行");
    }
}
